package cn.echo.ecc.service.impl;

import cn.echo.ecc.domain.AlipayInfo;
import cn.echo.ecc.domain.PayFlow;
import cn.echo.ecc.domain.PayOrder;
import cn.echo.ecc.dto.AlipayNotifyDto;
import cn.echo.ecc.dto.PayApplyDto;
import cn.echo.ecc.dto.PayApplyNotifyTxParamDto;
import cn.echo.ecc.dto.PayExtParamDto;
import cn.echo.ecc.enums.GlobalExceptionCode;
import cn.echo.ecc.exception.GlobalCustomException;
import cn.echo.ecc.result.JSONResult;
import cn.echo.ecc.service.IAlipayInfoService;
import cn.echo.ecc.service.IPayOrderService;
import cn.echo.ecc.service.IPayService;
import cn.echo.ecc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.factory.Factory.Payment;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

@Service
@Slf4j
public class PayServiceImpl implements IPayService {

    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IAlipayInfoService alipayInfoService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 根据订单编号回查支付单是否存在
     * @param orderNo
     * @return
     */
    @Override
    public JSONResult checkPayOrder(String orderNo) {
        return payOrderService.selectByOrderNo(orderNo) == null ? JSONResult.error() : JSONResult.success();
    }

    /**
     * 支付
     * @param payApplyDto
     * @return
     */
    @Override
    public String apply(PayApplyDto payApplyDto) {
        // 1.参数校验JSR303
        // 2.业务校验：支付单是否存在，支付状态是否为待支付
        String orderNo = payApplyDto.getOrderNo();
        PayOrder payOrder = payOrderService.selectByOrderNo(orderNo);
        AssertUtil.isNotNull(payOrder, GlobalExceptionCode.PAY_ORDER_NOT_EXIST_ERROR);
        boolean state = payOrder.getPayStatus() == PayOrder.WAIT_PAY_STATUS;
        AssertUtil.isTrue(state,GlobalExceptionCode.PAY_ORDER_STATE_ERROR);
        // 3.判断支付方式
        if (payApplyDto.getPayType() == PayOrder.ALIPAY) {
            log.info("开始调用支付宝API进行支付，订单：{}",orderNo);
            return alipay(payOrder,payApplyDto.getCallUrl());
        }
        return null;
    }

    /**
     * 支付宝异步回调业务处理
     * @param alipayNotifyDto
     * @return
     */
    @Override
    public String notify(AlipayNotifyDto alipayNotifyDto) {
        try {
            // 1.校验签名
            Map map = JSON.parseObject(JSON.toJSONString(alipayNotifyDto), Map.class);
            Boolean verifyNotify = Payment.Common().verifyNotify(map);
            if (!verifyNotify) {
                return "success";
            }
            // 2.校验交易支付是否成功
            boolean tradeState = alipayNotifyDto.getTrade_status().equals(AlipayNotifyDto.TRADE_SUCCESS);
            if (!tradeState) {
                return "success";
            }
            // 3.校验支付单是否存在
            PayOrder payOrder = payOrderService.selectByOrderNo(alipayNotifyDto.getOut_trade_no());
            if (payOrder == null) {
                return "success";
            }
            // 4.校验订单金额和支付金额是否一致
            if (payOrder.getAmount().compareTo(new BigDecimal(alipayNotifyDto.getTotal_amount())) != 0) {
                return "success";
            }
            // 5.校验支付单状态是否为支付取消和支付失败
            if (payOrder.getPayStatus() == PayOrder.CANCEL_STATUS || payOrder.getPayStatus() == PayOrder.FAIL_STATUS) {
                // 退款
                AlipayTradeRefundResponse refund = Payment.Common().refund(alipayNotifyDto.getOut_trade_no(), alipayNotifyDto.getTotal_amount());
                if (!refund.getCode().equals("10000")) {
                    // 不成功再重试3次，失败发邮件，站内信，钉钉通知运维维护
                   log.error("退款失败！订单：{}",payOrder.getOrderNo());
                } else {
                    log.info("退款成功！");
                    return "success";
                }
            }
            // 6.校验支付单状态是否为支付成功
            if (payOrder.getPayStatus() == PayOrder.SUCCESS_STATUS) {
                return "success";
            }
            // 7.发送RocketMQ事务消息
            PayApplyNotifyTxParamDto payApplyNotifyTxParamDto = initPayApplyNotifyTxParamDto(payOrder,alipayNotifyDto);
            Message<String> message = MessageBuilder.withPayload(payOrder.getExtParams()).build();
            TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                    "tx-pay-notify-producer-group",
                    "tx-pay-notify-topic:tx-pay-notify-tag",
                    message,
                    JSON.toJSONString(payApplyNotifyTxParamDto));
            boolean sendState = sendResult.getSendStatus() == SendStatus.SEND_OK;
            if (!sendState) {
                return "fail";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
        return "success";
    }

    /**
     * 封装事务参数：要修改的支付单和新增支付流水
     * @param payOrder
     * @param alipayNotifyDto
     * @return
     */
    private PayApplyNotifyTxParamDto initPayApplyNotifyTxParamDto(PayOrder payOrder, AlipayNotifyDto alipayNotifyDto) {
        try {
            payOrder.setUpdateTime(new Date());
            payOrder.setPayStatus(PayOrder.SUCCESS_STATUS);
            PayFlow payFlow = new PayFlow();
            payFlow.setCode(alipayNotifyDto.getCode());
            payFlow.setMsg(alipayNotifyDto.getMsg());
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = simpleDateFormat.parse(alipayNotifyDto.getNotify_time());
            payFlow.setNotifyTime(date);
            payFlow.setOutTradeNo(alipayNotifyDto.getOut_trade_no());
            payFlow.setPassbackParams(alipayNotifyDto.getPassback_params());
            payFlow.setPaySuccess(true);
            payFlow.setResultDesc(alipayNotifyDto.getBody());
            payFlow.setSubject(alipayNotifyDto.getSubject());
            payFlow.setTotalAmount(new BigDecimal(alipayNotifyDto.getTotal_amount()));
            payFlow.setTradeStatus(alipayNotifyDto.getTrade_status());
            return new PayApplyNotifyTxParamDto(payOrder,payFlow);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 支付宝付款
     * @param payOrder
     * @param callUrl
     * @return
     */
    private String alipay(PayOrder payOrder, String callUrl) {
        AlipayInfo alipayInfo = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(alipayInfo));
        try {
            // 2. 发起API调用（以创建当面付收款二维码为例）
            AlipayTradePagePayResponse response = Factory.Payment.Page().pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    callUrl
            );
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                log.info("支付宝支付调用成功");
                return response.getBody();
            } else {
                log.error("调用失败" + response.getBody());
            }
        } catch (Exception e) {
            log.error("调用遭遇异常");
            throw new GlobalCustomException(GlobalExceptionCode.ALIPAY_ERROR);
        }
        return null;
    }

    /**
     * 支付宝参数设置
     * @param alipayInfo
     * @return
     */
    private static Config getOptions(AlipayInfo alipayInfo) {
        Config config = new Config();
        config.protocol = alipayInfo.getProtocol();
        config.gatewayHost = alipayInfo.getGatewayHost();
        config.signType = alipayInfo.getSignType();
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();
        return config;
    }
}
